namespace Smarts.Core
{
    using System;

    /// <summary>
    /// Defines a range between to values.
    /// </summary>
    /// <typeparam name="T">The type of values in the range.</typeparam>
    public class Range<T> : IEquatable<Range<T>> where T : IComparable
    {
        private readonly T _lowerBound;
        private readonly T _upperBound;

        /// <summary>
        /// Initializes a new instance of the <see cref="Range&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="lowerBound">The lower bound.</param>
        /// <param name="upperBound">The upper bound.</param>
        public Range(T lowerBound, T upperBound)
        {
            _lowerBound = lowerBound;
            _upperBound = upperBound;
        }

        /// <summary>
        /// Gets the lower bound.
        /// </summary>
        /// <value>The lower bound.</value>
        public T LowerBound
        {
            get
            {
                return _lowerBound;
            }
        }

        /// <summary>
        /// Gets the upper bound.
        /// </summary>
        /// <value>The upper bound.</value>
        public T UpperBound
        {
            get
            {
                return _upperBound;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this range is empty.
        /// </summary>
        /// <value><c>true</c> if this range is empty; otherwise, <c>false</c>.</value>
        public bool IsEmpty
        {
            get
            {
                return _lowerBound.CompareTo(_upperBound) == 0;
            }
        }

        /// <summary>
        /// Determines whether the range contains the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>
        /// <c>true</c> if the range contains the specified value; otherwise, <c>false</c>.
        /// </returns>
        public bool Contains(T value)
        {
            return value.CompareTo(_lowerBound) >= 0 && value.CompareTo(_upperBound) <= 0;
        }

        /// <summary>
        /// Determines whether the range intersects with another range.
        /// </summary>
        /// <param name="range">The range.</param>
        /// <returns><c>true</c> if the range intersects with the specified range; otherwise, <c>false</c>.</returns>
        public bool Intersects(Range<T> range)
        {
            return (range._lowerBound.CompareTo(_lowerBound) > 0 && range._lowerBound.CompareTo(_upperBound) < 0)
                   || (range._upperBound.CompareTo(_lowerBound) > 0 && range._upperBound.CompareTo(_upperBound) < 0)
                   || Equals(range);
        }

        /// <summary>
        /// Determines whether the range contains the specified range.
        /// </summary>
        /// <param name="range">The range.</param>
        /// <returns>
        /// 	<c>true</c> if the range contains the specified range; otherwise, <c>false</c>.
        /// </returns>
        public bool Contains(Range<T> range)
        {
            return range._lowerBound.CompareTo(_lowerBound) >= 0 && range._upperBound.CompareTo(_upperBound) <= 0;
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        public bool Equals(Range<T> other)
        {
            return _lowerBound.CompareTo(other._lowerBound) == 0 && _upperBound.CompareTo(other._upperBound) == 0;
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return _lowerBound + " - " + _upperBound;
        }
    }

    /// <summary>
    /// Helper class for instantiating ranges.
    /// </summary>
    public static class Range
    {
        /// <summary>
        /// Creates the specified range.
        /// </summary>
        /// <typeparam name="T">The type of the range.</typeparam>
        /// <param name="lowerBound">The lower bound.</param>
        /// <param name="upperBound">The upper bound.</param>
        /// <returns>A new range with the specified bounds.</returns>
        public static Range<T> Create<T>(T lowerBound, T upperBound) where T : IComparable
        {
            return new Range<T>(lowerBound, upperBound);
        }
    }
}